using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using FilteredStatistics.Common;
using FilteredStatistics.Common.Controller;
using FilteredStatistics.Controller;

namespace FilteredStatistics.Data
{
    class FilterList : IPluginSerializable, IEnumerable<Filter>
    {
        public FilterList(IActivity activity)
        {
        }

#region IPluginSerializable members

        public override void Serialize(Stream stream)
        {
            SerializableInt32Range filterCount = new SerializableInt32Range(GetSerializableFilterCount());

            filterCount.Serialize(stream);

            foreach (Filter currentFilter in m_Filters)
            {
                if (currentFilter.IsValid &&
                    currentFilter.CriteriaFacade.IsSerializable)
                {
                    currentFilter.Serialize(stream);
                }
            }
        }

#endregion

#region Deserialization methods

        public void Deserialize_V1(Stream stream, DataVersion version)
        {
            FilterList loadedFilters = new FilterList(null);
            SerializableInt32Range filterCount = new SerializableInt32Range(0);

            filterCount.Deserialize(stream, version);

            m_Filters.Clear();

            for (int i = 0; i < filterCount; ++i)
            {
                Filter newFilter = new Filter();

                newFilter.Deserialize(stream, version);

                if (newFilter.IsValid)
                {
                    AddFilter(newFilter);
                }
            }
        }

#endregion

#region IEnumerable Members

        public IEnumerator<Filter> GetEnumerator()
        {
            return m_Filters.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return m_Filters.GetEnumerator();
        }

#endregion

        protected virtual void OnFilterChanged(Filter filter, PropertyChangedEventArgs changedProperty)
        {
            TriggerFilterChangedEvent(filter, changedProperty);
        }

        protected void TriggerFilterChangedEvent(Filter filter, PropertyChangedEventArgs propertyName)
        {
            if (FilterChanged != null)
            {
                FilterChanged(filter, propertyName);
            }
        }

        protected void TriggerFilterListChangedEvent(string propertyName)
        {
            if (FilterListChanged != null)
            {
                FilterListChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public virtual FilterList Clone()
        {
            FilterList copy = new FilterList(Activity);

            copy.m_Filters.Clear();
            foreach (Filter currentFilter in m_Filters)
            {
                copy.AddFilter(currentFilter.Clone());
            }

            return copy;
        }

        public void Clear()
        {
            foreach (Filter filter in m_Filters)
            {
                filter.FilterChanged -= new Filter.FilterChangedEventHandler(OnFilterChanged);
            }

            m_Filters.Clear();

            Setup();
        }

        public void Setup()
        {
            if (m_Filters.Count == 0 || !m_Filters[m_Filters.Count - 1].IsValid)
            {
                AddFilter(new Filter());
            }
        }

        public bool Contains(Filter filter)
        {
            return m_Filters.Contains(filter);
        }

        public virtual void AddFilter(Filter filter)
        {
            Debug.Assert(!m_Filters.Contains(filter));

            m_Filters.Add(filter);
            filter.FilterChanged += new Filter.FilterChangedEventHandler(OnFilterChanged);

            // Don't trigger the event when we add an empty filter (placeholder for editing)
            if (filter.CriteriaFacade != null)
            {
                TriggerFilterListChangedEvent("Filters");
            }
            else
            {
                TriggerFilterListChangedEvent("NewPlaceholderFilter");
            }
        }

        public virtual void RemoveFilter(int index)
        {
            Debug.Assert(m_Filters.Count > index);

            m_Filters[index].FilterChanged -= new Filter.FilterChangedEventHandler(OnFilterChanged);
            m_Filters.RemoveAt(index);

            TriggerFilterListChangedEvent("Filters");
        }

        public Filter GetFilter(int index)
        {
            Debug.Assert(m_Filters.Count > index);

            return m_Filters[index];
        }

        public int GetFilterCount()
        {
            return m_Filters.Count;
        }

        public int GetSerializableFilterCount()
        {
            int total = 0;

            foreach (Filter currentFilter in m_Filters)
            {
                if (currentFilter.IsValid &&
                    currentFilter.CriteriaFacade.IsSerializable)
                {
                    total++;
                }
            }

            return total;
        }

        public virtual FilterResults GetMatchingRanges()
        {
            List<IFilteredDataSet> filteredDataSet = new List<IFilteredDataSet>();

            // We must now recreate new data for our missing entries
            if (Activity != null)
            {
                for (int i = 0; i < m_Filters.Count; ++i)
                {
                    if (m_Filters[i].CriteriaFacade != null)
                    {
                        // Create a new set of entries from our last valid cached filter data
                        IFilteredDataSet newFilteredData = null;
                        IFilteredDataSet previousDataSet = null;

                        if (i == 0)
                        {
                            previousDataSet = WholeActivityFilteredDataSet.Instance;
                        }
                        else
                        {
                            previousDataSet = filteredDataSet[i - 1];
                        }

                        newFilteredData = new BasicFilteredDataSet(Activity, m_Filters[i], previousDataSet.GetFilteredTimes());
                        filteredDataSet.Add(newFilteredData);
                    }
                }

                if (filteredDataSet.Count > 0)
                {
                    return filteredDataSet[filteredDataSet.Count - 1].GetFilteredTimes();
                }
                else
                {
                    return WholeActivityFilteredDataSet.Instance.GetFilteredTimes();
                }
            }
            else
            {
                return new FilterResults();
            }
        }

        public bool ContainsTemplateLoop(Template template)
        {
            foreach (Filter filter in m_Filters)
            {
                if (filter.IsValid &&
                    filter.CriteriaFacade is TemplateFilterCriteria)
                {
                    foreach (TemplateNamedZone namedZone in filter.Zones)
                    {
                        if (namedZone.Template.Filters.ContainsTemplate(template))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        public bool ContainsTemplate(Template template)
        {
            foreach (Filter filter in m_Filters)
            {
                if (filter.IsValid &&
                    filter.CriteriaFacade is TemplateFilterCriteria)
                {
                    foreach (TemplateNamedZone namedZone in filter.Zones)
                    {
                        if (namedZone.Template == template)
                        {
                            return true;
                        }
                        else if (namedZone.Template.Filters.ContainsTemplate(template))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        public bool ValidateFiltersConsistency()
        {
            bool filterModified = false;

            // Go through our filter list and check if we use the modified zone
            for (int i = 0; i < GetFilterCount(); ++i)
            {
                Filter currentFilter = GetFilter(i);

                if (currentFilter.CriteriaFacade != null)
                {
                    IFilterCriteria criteria = currentFilter.CriteriaFacade;

                    if (FilterCriteriaController.Instance.GetCriteria(criteria.ReferenceId) == null ||
                        !criteria.ValidateConsistency())
                    {
                        // Remove current filter altogether
                        RemoveFilter(i);

                        --i;
                        filterModified = true;
                    }
                }
            }

            return filterModified;
        }

        public virtual IActivity Activity
        {
            get { return m_ActivityToFilter; }
            set
            {
                if (m_ActivityToFilter != value)
                {
                    m_ActivityToFilter = value;

                    foreach (Filter filter in m_Filters)
                    {
                        if (filter.CriteriaFacade != null)
                        {
                            filter.CriteriaFacade.Activity = Activity;
                        }
                    }
                }
            }
        }

        public delegate void FilterListChangedEventHandler(FilterList filterList, PropertyChangedEventArgs changedProperty);
        public event FilterListChangedEventHandler FilterListChanged;

        public delegate void FilterChangedEventHandler(Filter filter, PropertyChangedEventArgs changedProperty);
        public event FilterChangedEventHandler FilterChanged;

        protected List<Filter> m_Filters = new List<Filter>();
        private IActivity m_ActivityToFilter;
    }
}
